/*-
 * Copyright (c) 2012-2013 Shang-Rong Cai. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $ MTBLAS: exec_isa.h    Rev. 0.0.24    2013-11-09T10:11+08:00 $
 */

#include <stddef.h>

#ifndef EXEC_ISA_H
#define EXEC_ISA_H

#define MTBLAS_ISA_VERSION 201311090001

/* Address Type */
#ifndef ADDR_TYPE
#define ADDR_TYPE
typedef void* addr_t;
#endif

/* Step Type */
#ifndef STEP_TYPE
#define STEP_TYPE
typedef ptrdiff_t step_t;
#endif


/* Operand Type */
typedef struct
{
    addr_t addr;  /* Memory address */
    step_t step;  /* Step size unit byte */
} operand_t;


/* Operations Execution Pack Type */
typedef struct
{
    addr_t oc;  /* Pointer to opocde */
    size_t sz;  /* Execution size */

    operand_t ra;  /* Operand %a */
    operand_t rb;  /* Operand %b */
    operand_t rc;  /* Operand %c */
    operand_t rd;  /* Operand %d */
} opchunk_t;


/* Execution */
extern void execute(void *oc, const size_t sz, operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);


/* Threads Execution */
#define THREADS_MIN 1
#define THREADS_MAX 8

extern int thread_execute(size_t tz, void *oc, const size_t sz, operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);


extern size_t proper_thrz(size_t thrz);


extern int operands_distributor(size_t thrz, size_t sz, void *addr, step_t step, operand_t *ra);

extern int operands_collector(size_t thrz, size_t typz, void *addr, operand_t *ra);

extern int operands_batch_set(size_t thrz, void *addr, step_t step, operand_t *ra);


extern int operands_create_2(size_t thrz, operand_t **ra, operand_t **rb);

extern int operands_create_3(size_t thrz, operand_t **ra, operand_t **rb, operand_t **rc);

extern int operands_create_4(size_t thrz, operand_t **ra, operand_t **rb, operand_t **rc, operand_t **rd);


extern int operands_delete_2(operand_t *ra, operand_t *rb);

extern int operands_delete_3(operand_t *ra, operand_t *rb, operand_t *rc);

extern int operands_delete_4(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);


extern int operands_assign_single_opaddr(size_t thrz, operand_t *op, void *addr);

extern int operands_assign_opchunks(size_t thrz, operand_t *op, opchunk_t *chunk);


extern int opchunks_create_3(size_t thrz, opchunk_t **pk_a, opchunk_t **pk_c, opchunk_t **pk_d);

extern int opchunks_delete_3(opchunk_t *pk_a, opchunk_t *pk_c, opchunk_t *pk_d);

extern int opchunks_set_opcode(size_t thrz, opchunk_t *chunk, addr_t oc);

extern int opchunks_set_opsize(size_t thrz, opchunk_t *chunk, size_t sz);


extern int opchunks_set_ra_addr_mono(size_t thrz, opchunk_t *chunk, addr_t addr);

extern int opchunks_set_ra_addr_dist(size_t thrz, opchunk_t *chunk, size_t sz, addr_t addr, step_t stride);

extern int opchunks_set_ra_step_mono(size_t thrz, opchunk_t *chunk, step_t step);


extern int opchunks_set_rb_addr_mono(size_t thrz, opchunk_t *chunk, addr_t addr);

extern int opchunks_set_rb_addr_dist(size_t thrz, opchunk_t *chunk, size_t sz, addr_t addr, step_t stride);

extern int opchunks_set_rb_step_mono(size_t thrz, opchunk_t *chunk, step_t step);


extern int opchunks_set_rc_addr_mono(size_t thrz, opchunk_t *chunk, addr_t addr);

extern int opchunks_set_rc_addr_dist(size_t thrz, opchunk_t *chunk, size_t sz, addr_t addr, step_t stride);

extern int opchunks_set_rc_step_mono(size_t thrz, opchunk_t *chunk, step_t step);


extern int opchunks_set_rd_addr_mono(size_t thrz, opchunk_t *chunk, addr_t addr);

extern int opchunks_set_rd_addr_dist(size_t thrz, opchunk_t *chunk, size_t sz, addr_t addr, step_t stride);

extern int opchunks_set_rd_step_mono(size_t thrz, opchunk_t *chunk, step_t step);


/* Special Opcode */
/* Show Meta of Operands */
extern void op4_meta_op(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* Combine execute() and opcode */
/*
 * ra: opchunk_t; target address of execution: opcode, counts, operands.
 * rb: opchunk_t; for modifying target, update target before executing ra.
 * rc: opchunk_t; set/reset address value (change after run)
 * rd: opchunk_t; set/reset step value (change after run)
 *
 * Remark:
 * The modifying target will be update from the outer to inner when using this
 * opocde nestedly.  The outer will update before running inner.
 */
extern void op4_exec_op(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

#ifdef DEBUG_INFO
extern void debug_op4_exec_op(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
#endif


/* Execute Opcode 2 */
extern void op3_eoc2_op(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);


/* Operation Code - 2 Operands */
/* swap(%a, %b) */
extern void op2_swap_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_swap_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_swap_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op2_swap_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_swap_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_swap_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* %b = %a */
extern void op2_copy_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_copy_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_copy_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op2_copy_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_copy_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_copy_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* %b = abs(%a) */
extern void op2_fabs_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_fabs_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_fabs_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* accumulation: %b += %a or %b = %a + %b */
extern void op2_accum_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_accum_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_accum_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op2_accum_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_accum_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_accum_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* deduction: %b -= %a or %b = %b - %a */
extern void op2_deduct_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_deduct_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_deduct_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op2_deduct_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_deduct_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_deduct_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* scaling: %b = %a * %b */
extern void op2_scale_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_scale_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_scale_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op2_scale_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_scale_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_scale_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* %b = sum(vector(%a)) */
extern void op2_vsum_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vsum_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vsum_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op2_vsum_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vsum_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vsum_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* %b = sum(vector(abs(%a))) */
extern void op2_vabssum_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vabssum_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vabssum_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op2_vabssum_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vabssum_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vabssum_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* %b = max(vector(abs(%a))) where %b is address */
extern void op2_vabsmax_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vabsmax_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_vabsmax_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* %b = conj(%a) */
extern void op2_conjcopy_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_conjcopy_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op2_conjcopy_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);


/* Operation Code - 3 Operands */
/* %c = %a + %b */
extern void op3_add_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_add_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_add_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op3_add_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_add_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_add_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* %c = %a * %b */
extern void op3_mul_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_mul_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_mul_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op3_mul_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_mul_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_mul_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* Fused Multiply Accumulate (FMAC): %c += %a * %b */
extern void op3_fmac_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_fmac_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_fmac_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op3_fmac_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_fmac_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_fmac_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* Conjugated Complex Fused Multiply Accumulate (Conj-FMAC) %c += conj(%a) * %b */
extern void op3_conjfmac_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_conjfmac_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_conjfmac_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* Conjugated Complex Fused Multiply Accumulate 2 (Conj-FMAC) %c += %a * conj(%b) */
extern void op3_conjbfmac_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_conjbfmac_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_conjbfmac_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* Conjugated Complex Fused Multiply Accumulate 3 (Conj-FMAC) %c += conj(%a) * conj(%b) */
extern void op3_conjabfmac_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_conjabfmac_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_conjabfmac_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);


/* %c = abs(%a) + abs(%b) */
extern void op3_fabsadd_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_fabsadd_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_fabsadd_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op3_cabsadd_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_cabsadd_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op3_cabsadd_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);


/* Operation Code - 4 Operands */
/* Fused Multiply Add(FMA): %d = %a * %b + %c */
extern void op4_fma_ff(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op4_fma_df(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op4_fma_lf(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

extern void op4_fma_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op4_fma_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op4_fma_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

/* Conjugated Complex Fused Multiply Add (Conj-FMA): %d = conj(%a) * %b + %c */
extern void op4_conjfma_fc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op4_conjfma_dc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);
extern void op4_conjfma_lc(operand_t *ra, operand_t *rb, operand_t *rc, operand_t *rd);

#endif
